#define DEBUG_THREAD_INFO
#include "thread_pool.hpp"
#include <iostream>

void hello(int threadNumber)
{
    std::cout << "hello threadId : " << threadNumber << std::endl;
}

int sum(int threadNumber, int x, int y, int z)
{
    return x + y + z;
}

void func()
{
    std::cout << "hello func()" << std::endl;
}

struct Object
{
    int _value;

    Object(const int &value) : _value(value) { std::cout << "Object : constructor!" << std::endl; }
    Object(int &&value) : _value(value) { std::cout << "Object : move constructor!" << std::endl; }
    Object(const Object &other) : _value(other._value) { std::cout << "Object : copy constructor" << std::endl; }
    ~Object() { std::cout << "Object : destructor!" << std::endl; }
}; // struct Object

std::string toUper(int threadNumber, const std::string &str)
{
    for (int i = 0, n = str.size(); i < n; ++i)
    {
        if (isalpha(str[i]) && islower(str[i]))
        {
            toupper(str[i]);
        }
    }
    return str;
}

void getValue(int id, Object &Object)
{
    std::this_thread::sleep_for(std::chrono::milliseconds(2000));
    std::cout << "function with parameter Third " << Object._value << std::endl;
}

int main()
{
    thread_pool::ThreadPool pool(5);
    pool.submit(std::ref(hello));
    pool.submit(hello);
    pool.submit(sum, 1, 2, 3);

    {
        struct Functor
        {
            Functor(const std::string &s)
            {
                std::cout << "Functor : constructor\n";
                _str = s;
            }
            Functor(Functor &&other)
            {
                std::cout << "Functor : move constructor\n";
                _str = std::move(other._str);
            }
            Functor(const Functor &other)
            {
                std::cout << "Functor : copy constructor\n";
                _str = other._str;
            };
            ~Functor() { std::cout << "Functor : destructor\n"; }
            void operator()(int threadNumber) const
            {
                std::cout << "Functor : " << _str << '\n';
            }

        private:
            std::string _str;
        } functor("functor");

        pool.submit(std::ref(functor));
        std::this_thread::sleep_for(std::chrono::milliseconds(2000));
        pool.submit(const_cast<const Functor &>(functor));
        pool.submit(std::move(functor));
        pool.submit(functor);
        pool.submit(Functor("functor"));
    }

    {
        Object object(100);
        pool.submit(getValue, std::ref(object));
        pool.submit(getValue, object);
        pool.submit(getValue, std::move(object));
    }

    std::string str = "Hello, lambda";
    pool.submit([str](int threadNumber)
                {std::this_thread::sleep_for(std::chrono::milliseconds(2000));std::cout << str << std::endl; });

    pool.submit([str](int threadNumber) mutable
                {std::this_thread::sleep_for(std::chrono::milliseconds(2000));std::cout << toUper(threadNumber, str) << std::endl; });

    pool.submit(toUper, "W\?thord#@");

    auto task = pool.pull();
    if(task)
    {
        std::cout << "poped function from the pool\n";
        task(0);
    }

    pool.resize(1);
    std::string s("res\[]ult");
    auto f1 = pool.submit([s](int threadNumber)mutable -> std::string { return toUper(threadNumber, s);});

    auto f2 = pool.submit([](int){
        throw std::exception();
    });
    try {
        f2.get();
    }
    catch (std::exception & e) {
        std::cout << "caught exception\n";
    }

    auto & thread = pool.getThread(0);
    std::cout << "thread number : " << 0 << thread.get_id() << std::endl;

    return 0;
}
